use axum::{
    body::Bytes,
    http::HeaderMap,
    routing::{get, post},
    Router,
};
use serde::{Deserialize, Serialize};

use crate::{
    error::{AppErr, EC_NO_DEVICE},
    server::{
        model::{cbor_ok, Cbor, CborRet, Void},
        msg,
        req::{get_header_value, parse_header_value},
    },
    store::device,
};

use super::MsgItem;

#[derive(Debug, Deserialize)]
struct LoginReq {
    mac_addr: String,
}

#[derive(Debug, Serialize)]
struct LoginRes {
    token: String,
    id: i64,
}

async fn on_login(req: Cbor<LoginReq>) -> CborRet<LoginRes> {
    let id = device::is_exist(&req.mac_addr)
        .await?
        .ok_or(AppErr::new_service(EC_NO_DEVICE, "没有注册该设备"))?;
    let token = msg::login(id, &req.mac_addr);
    cbor_ok(LoginRes { token, id })
}

async fn on_poll(header: HeaderMap) -> Result<MsgItem, AppErr> {
    let id: i64 = parse_header_value(&header, "id")?;
    let token = get_header_value(&header, "token")?;
    let item = msg::poll(id, token).await?;
    Ok(item)
}

async fn on_notify(header: HeaderMap, msg_item: Bytes) -> Result<Void, AppErr> {
    let id: i64 = parse_header_value(&header, "id")?;
    let cmd = get_header_value(&header, "cmd")?;
    msg::post_notify(id, cmd, msg_item).await?;
    Ok(Void)
}

async fn on_res(header: HeaderMap, msg_item: Bytes) -> Result<Void, AppErr> {
    let id: i64 = parse_header_value(&header, "id")?;
    let seq: u8 = parse_header_value(&header, "seq")?;
    msg::post_res(id, seq, msg_item)?;
    Ok(Void)
}

async fn on_req(header: HeaderMap, body: Bytes) -> Result<Bytes, AppErr> {
    let id: i64 = parse_header_value(&header, "id")?;
    let cmd = get_header_value(&header, "cmd")?;
    let timeout: u32 = parse_header_value(&header, "timeout")?;
    let res = msg::post_req(id, cmd, body, timeout).await?;
    Ok(res)
}

async fn on_poll_timestamp(header: HeaderMap) -> CborRet<u64> {
    let id: i64 = parse_header_value(&header, "id")?;
    let timestamp = msg::poll_timestamp(id)?;
    cbor_ok(timestamp)
}

pub fn register() -> Router {
    Router::new()
        .route("/poll", get(on_poll))
        .route("/login", post(on_login))
        .route("/notify", post(on_notify))
        .route("/req", post(on_req))
        .route("/res", post(on_res))
        .route("/poll_timestamp", get(on_poll_timestamp))
}
